/*
 * Jonathan Marokhovsky
 * WPI
 * Version: November 27, 2011
 * 
 * Nutella Main client process
 */

#include "listener.h"

#define REC_WAIT	5
#define EXIT_CMD	"exit"
#define DEBUG		0
#define WRONG_MAX	100 /* If the wrong message was received more than this
						 * then assume the right answer isn't coming.
						 */

int connectToStream(char *hostAddr, int port);

int main(void)
{
	pid_t theListener;
	char movieName[MAX_SEARCH];
	char replay[MAX_SEARCH];
	char received[MAX_RESP];
	char frame[MAX_FRAME];
	char *responsePart;
	char *streamIP;
	char handshake[MAX_SEARCH];
	int port;
	int exitFlag = 0;
	int qSock, rSock, tcpSock, streamSock, bytes, childStat, bytesRecv, found;
	int wrongCounter, repFlag;
	fd_set readFrom;
	struct timeval timeOut;

	timeOut.tv_sec = REC_WAIT;
	timeOut.tv_usec = 0;
	
	debug = DEBUG; // set the debugger to run for now
	
	/* First, fork to start the listener */
	if (debug) printf("NUTELLA: Creating listener process\n");
	if ((theListener = fork()) < 0)
	{
		perror("ERROR: forking");
		exit(EXIT_FAILURE);
	}
	else if (theListener == 0)
	{
		if (debug) printf("LISTENER: Starting listener.\n");
		listener();
	}
	else /* It's the parent process */
	{
		if (debug) printf("CLIENT: In the Nutella client process.\n");
		/* Open all multicast sockets before the loop starts */
		if (debug) printf("CLIENT: Openning all necessary sockets.\n");
		/* Open the Query and Receive sockets */
		if (debug) printf("CLIENT: Opening the Query socket.\n");
		if ((qSock = msockcreate(SEND, Q_ADDR, Q_PORT)) < 0)
		{
			perror("ERROR: query socket creation");
			exit(EXIT_FAILURE);
		}
		if (debug) printf("CLIENT: Opening the Receive socket.\n");
		if ((rSock = msockcreate(RECV, R_ADDR, R_PORT)) < 0)
		{
			perror("ERROR: receive socket creation");
			exit(EXIT_FAILURE);
		}
		
		/* Set the Receive socket to nonblocking so it can time out*/
		if (debug) printf("CLIENT: Setting the Receive socket to nonblocking.\n");
		if (fcntl(rSock, F_SETFL, O_NONBLOCK) < 0)
		{
			perror("ERROR: set nonblocking");
			exit(EXIT_FAILURE);
		}
		
		/* Start the Query loop */
		if (debug) printf("CLIENT: Starting loop...\n");
		while (exitFlag == 0)
		{	
			bytesRecv = 0; // initialize this to zero
			/* Prompt the user for a movie name */
			printf("Type in the movie you would like to see: \n");
			scanf("%s", movieName);
			if (debug) printf("CLIENT: The movie name was '%s'\n", movieName);
			
			/* if it's the exit command, set the exit flag and break */
			if (strncmp(movieName, EXIT_CMD, strlen(EXIT_CMD)) == 0)
			{
				if (debug) printf("CLIENT: Exiting...\n");
				exitFlag++;
				break;
			}
			
			/* Send the request for the movie name on the Query socket*/
			if (debug) printf("CLIENT: Sending out the request for '%s'.\n", movieName);
			if (msend(qSock, movieName, strlen(movieName) + 1) < 0)
			{
				perror("ERROR: sending query");
				exit(EXIT_FAILURE);
			}
						
			/* Listen on the Receive socket for any responses */
			/* Add the Receive socket to the readset */
			if (debug) printf("CLIENT: Adding the Receive socket to a receive set.\n");
			FD_ZERO(&readFrom);
			if (FD_SET(rSock, &readFrom) < 0)
			{
				perror("ERROR: adding rsock to readset");
				exit(EXIT_FAILURE);
			}
			found = 0;
			wrongCounter = 0;
			do
			{
				/* Wait for a response */
				bytesRecv = select(rSock + 1, &readFrom, NULL, NULL, &timeOut);
				if (bytesRecv == 0) // if select returns 0 it means it timed out.
				{
					if (debug) printf("CLIENT: Select returned 0.\n");
					printf("'%s' cannot be found at this time. Try again later.\n", movieName);
					break;
				}
				if (debug) printf("CLIENT: There was a response.\n", bytesRecv);
				/* If the receive socket is set, there was a response */
				if (FD_ISSET(rSock, &readFrom))
				{
					if (debug) printf("CLIENT: Checking the response.\n");
					/* Check the Receive socket for the message */
					if (mrecv(rSock, received, MAX_RESP + 1) < 0)
					{
						perror("ERROR: receiving response");
						exit(EXIT_FAILURE);
					}
					if (debug) printf("CLIENT: Got this as a response: '%s'\n", received);
					
					/* Check that it is the right response */
					if (debug) printf("CLIENT: Checking that this is the right message.\n");
					responsePart = strtok(received, DELIM);
					if (debug) printf("CLIENT: The response was: %s\n", responsePart);
					if ((strncmp(movieName, responsePart, strlen(movieName) + 1)) == 0)
					{
						if (debug) printf("CLIENT: It is the right string, getting the info to connect.\n");
						found++;
						/* Parse out the remainder of the info needed */
						streamIP = strtok(NULL, DELIM);
						port = atoi(strtok(NULL, DELIM));
						if (debug) printf("CLIENT: The info is: %s: %d\n", streamIP, port);
						
						/* Send a TCP request for the movie to the extracted IP address and port */
						if (debug) printf("CLIENT: Connecting to the server.\n");
						if ((streamSock = connectToStream(streamIP, port)) < 0)
						{
							perror("ERROR: connecting to stream");
							exit(EXIT_FAILURE);
						}
						/* Start a player loop, loop until the EOF has been reached */
						do
						{
							repFlag = 0; // reset this
							if (debug) printf("CLIENT: Now getting frames from the server.\n");
							while ((bytes = read(streamSock, frame, MAX_FRAME)) > 0)
							{
								/* If a frame was received, clear the screen and display the new frame */
								printf("\033[2J");
								printf("\033[0;0f");
								if (strncmp(frame, "end", 3) != 0)
								{
									printf("%s", frame);
								}
								else
								{
									if (debug) printf("CLIENT: End of movie reached\n");
									break;
								}
							}
							/* Receive from the server half of a handshake */
							if (debug) printf("CLIENT: Reading from the socket.\n");
							if ((bytes = read(streamSock, handshake, MAX_SEARCH)) < 0)
							{
								perror("ERROR: reading handshake");
								exit(EXIT_FAILURE);
							}
							if (debug) printf("CLIENT: Received this as the handshake: %s.\n", handshake);
							printf("Replay? y/n\n");
							scanf("%s", replay);
							/*
							 * If the response starts with 'y', respond with the
							 * same handshake.
							 */
						 	if (debug) printf("CLIENT: Received this from the user: %s\n", replay);
						 	if (strncmp(replay, "y", 1) == 0)
						 	{
						 		repFlag++; // return to the start of the loop
								/* Send the response to the server */
								if (debug) printf("CLIENT: Replying to the server\n");
								if (write(streamSock, handshake, strlen(handshake) + 1) < 0)
								{
									perror("ERROR: replying with handshake");
									exit(EXIT_FAILURE);
								}
						 	}
						 	else
						 	{
								/* Otherwise, let the connection die and start looping again */
								if (debug) printf("CLIENT: User did not say anything starting with 'y'\n");
						 	}
						} while (repFlag > 0);
					}
					else
					{
						/* Continue searching */
						if (debug) printf("CLIENT: This was not the right message.\n");
						if (debug) printf("CLIENT: '%s' != '%s'\n", movieName, responsePart);
						wrongCounter++; // If this gets too high, the loop will end
					}
				}
				else
				{
					if (debug) printf("CLIENT: Connection timeout\n");
					if (debug) printf("CLIENT: bytes received: %d, found: %d\n", bytesRecv, found);
				}
			} while ((found == 0) || (wrongCounter == WRONG_MAX));
			/* Return to the start of the loop */
		}
		/* Close all multicast sockets */
		if (debug) printf("CLIENT: Destroying the Query socket.\n");
		if (msockdestroy(qSock) < 0)// close the Query socket
		{
			perror("ERROR: query socket destruction");
			exit(EXIT_FAILURE);
		}
		
		if (debug) printf("CLIENT: Destroying the Receive socket.\n");
		if (msockdestroy(rSock) < 0)// close the Receive socket
		{
			perror("ERROR: receive socket destruction");
			exit(EXIT_FAILURE);
		}
		/* Wait here until the child process dies */
		if (debug) printf("CLIENT: Waiting for LISTENER to terminate.\n");
		killpg(0, SIGKILL);
		wait(&childStat);	
	}
	return 0; // the program has successfully run
}

/*
 * connectToStream() connects to the given address at the given port
 * 
 * This returns the socket if it succeeded and -1 if there was an error
 *
 * Note: this was taken from my Project 2 client.c nearly word for word
 */
int connectToStream(char *hostAddr, int port)
{
	int sock = 0;
	struct sockaddr_in addr;
	struct hostent *hp;
	
	/*
	 * Try to convert the host name as a dotted-decimal number.
	 * If that fails, use gethostbyname().
	 */
	bzero((void *) &addr, sizeof(addr));
	if (debug) printf("CLIENT: Looking up %s...\n", hostAddr);
	if ((hp = gethostbyname(hostAddr)) == NULL)
	{
		perror("ERROR: host name error");
		sock = EXIT_FAILURE;
	}
	else
	{
		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
		
		if (debug) printf("CLIENT: Found it. Setting port communcation to %d...\n", port);
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		
		/* Open a TCP socket */
		if (debug) printf("CLIENT: Done. Opening socket...\n");
		if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		{
			perror("ERROR: opening socket");
			sock = EXIT_FAILURE;
		}
		else
		{
			if (debug) printf("CLIENT: Open. Trying connection to server...\n");
			if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
			{
				perror("ERROR: can't connect");
				sock = EXIT_FAILURE;
			}
			else
			{
				if (debug) printf("CLIENT: Connected to: %s: %d\n", hostAddr, port);
				printf("Movie was found! Playing now...\n");
			}
		}
	}
	return sock;
}

